下面这个是最朴素的继承方式。基类Employee
有用来计算工资的两个域,和一个计算工资的函数。子类Manager
增加了计算工资需要的两个额外的域,然后覆盖了计算工资的函数。就这么简单。
class Employee {
protected final int baseSalary;
protected final int years;
public Employee(int baseSalary, int years) {
this.baseSalary = baseSalary;
this.years = years;
}
public int getSalary() { // 普通员工工资算法
return baseSalary + (100 * years);
}
// reminder omitted
}
class Manager extends Employee {
protected final int baseBonus;
protected final int level;
public Manager(int salary, int years, int bonus, int level) {
super(salary,years);
baseBonus = bonus;
this.level = level;
}
public int getSalary() { // 管理层的工资算法
return super.getSalary() + (baseBonus * level);
}
// remainder omitted
}
站在一个Employee
的角度看,它需要管理它的2个域baseSalary
和years
。
站在一个Manager
的角度看,它需要管理它全部的4个域baseSalary
和years
,以及baseBonus
和level
。
这样会不会很累?Employee
能不能管好它的两个域。然后通过公共接口对外提供对内部域的有限访问。这样Manager
可以通过Employee
来管理前两个域,它自己只需要管理后两个域。
变化其实很简单,我只是私有化了Employee
中的两个域,以及Manager
中的两个域。
这样Manager
不需要实际继承Employee
的所有域,它只需要通过getSalary()
接口访问Employee
对象提供的服务。
class Employee {
private final int baseSalary;
private final int years;
public Employee(int baseSalary, int years) {
this.baseSalary = baseSalary;
this.years = years;
}
public int getSalary() { // 普通员工工资算法
return baseSalary + (100 * years);
}
// reminder omitted
}
class Manager extends Employee {
private final int baseBonus;
private final int level;
public Manager(int salary, int years, int bonus, int level) {
super(salary,years);
baseBonus = bonus;
this.level = level;
}
public int getSalary() { // 管理层的工资算法
return super.getSalary() + (baseBonus * level);
}
// remainder omitted
}
只需要把上面的两个域baseSalary
和years
替换成Employee
对象,继承就变成了组合。
class Employee {
private final int baseSalary;
private final int years;
public Employee(int baseSalary, int years) {
this.baseSalary = baseSalary;
this.years = years;
}
public int getSalary() { // 普通员工工资算法
return baseSalary + (100 * years);
}
// reminder omitted
}
class Manager {
private final Employee employee;
private final int baseBonus;
private final int level;
public Manager(Employee employee, int bonus, int level) {
this.employee = employee;
baseBonus = bonus;
this.level = level;
}
public int getSalary() { // 管理层的工资算法
return employee.getSalary() + (baseBonus * level);
}
// remainder omitted
}
组合很容易转换成装饰器模式。只需把前一个对象的引用传递给后一个类的构造器。和组合一样,每一层装饰只负责它管理的几个域。这样对数据的封装就比较合理。
interface Employee {
public int getSalary();
}
class BaseEmployee implements Employee {
private final int baseSalary;
private final int years;
public BaseEmployee(int baseSalary, int years) {
this.baseSalary = baseSalary;
this.years = years;
}
public int getSalary() { // 普通员工工资算法
return baseSalary + (100 * years);
}
}
class TrafficAllowance implements Employee {
private final Employee employee;
private final int allowance;
public TrafficAllowance(Employee employee, int allowance) {
this.employee = employee;
this.allowance = allowance;
}
public int getSalary() { // 管理层的工资算法
return employee.getSalary() + allowance;
}
}
class ManagerBonus implements Employee {
private final Employee employee;
private final int baseBonus;
private final int level;
public ManagerBonus(Employee employee, int bonus, int level) {
this.employee = employee;
baseBonus = bonus;
this.level = level;
}
public int getSalary() { // 管理层的工资算法
return employee.getSalary() + (baseBonus * level);
}
}
总的来说,虽然封装的方式可以千变万化,但 组合优于继承 的理念背后的逻辑还是很清楚:
组合类面对的是封装好的对象。不需要费心去管理这个对象,只要调用接口提供的服务就可以了。
但继承是侵入式的,它破坏了基类的封装。。